Skip to content

Reference chains for surviving live-heap samples via a bounded manual heap walk (PROF-15341) - #644

Draft
jbachorik wants to merge 43 commits into
mainfrom
jb/reference-chains
Draft

Reference chains for surviving live-heap samples via a bounded manual heap walk (PROF-15341)#644
jbachorik wants to merge 43 commits into
mainfrom
jb/reference-chains

Conversation

@jbachorik

@jbachorik jbachorik commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?:
Implements reference-chain reconstruction for live-heap samples that survive
past their allocation window (PROF-15341). A ReferenceChainTracker runs a
dedicated BFS thread that tags reachable objects with JVMTI object tags,
walks the heap incrementally across GC epochs, and records the referrer-type
chain back toward a GC root (labelled with its GC-root kind). It is bridged
to LivenessTracker::selectLeakCandidates()'s population-slope leak
detection: when a still-live sampled object starts to look leak-shaped, its
chain is reconstructed from the frontier table and emitted to JFR.

flowchart TD
    LT["LivenessTracker::selectLeakCandidates (population-slope ranking)"] -->|"ranked klass candidates"| PWT["ReferenceChainTracker::pollWatchedTargets"]
    BFS["BFS thread: threadLoop"] -->|"shouldRunPass gate: GC epoch advanced or cadence elapsed"| RP["runPass"]
    RP -->|"every pass"| MW["runPassManualWalk: IterateOverReachableObjects seeds roots"]
    MW --> EF["expandFrontier: batched array-holder FollowReferences"]
    EF --> FT["FrontierTable: FRONTIER / EXPANDED / EDGE / ABANDONED"]
    FT -->|"tag already set on a leak-candidate instance"| PWT
    PWT -->|"buildChainEvent"| RC["cacheResolvedChain: one entry per klass id, cap 256"]
    RC -->|"Profiler::dump, snapshot without clearing"| DR["drainPendingChainEvents"]
    DR --> JFR["datadog.ReferenceChain / datadog.ReferenceChainAbandoned"]
Loading

Heap-walk mechanism (the main design decision in this branch): every pass
takes a bounded manual walk (runPassManualWalk()) — pure JVMTI
(IterateOverReachableObjects for root enumeration + expandFrontier() for
incremental frontier expansion), resumable across passes, and bounded so its
safepoint pauses stay small on every collector, including ZGC. The walk
issues only JVMTI heap calls, which run inside the VM_HeapWalkOperation
safepoint and honor ZGC's load barriers, so concurrent relocation cannot
corrupt it — it reads no raw oop.

expandFrontier()'s batching, concretely: instead of one FollowReferences
call per frontier entry, up to budget pending tags are resolved in one
GetObjectsWithTags call, packed into a single JNI array (holder), and
expanded via exactly one FollowReferences(initial_object = holder) call —
so one BFS level is discovered per VM-safepoint operation rather than one
safepoint per entry:

sequenceDiagram
    participant BFS as BFS thread
    participant JVMTI as JVMTI
    participant JNI as JNI holder array
    participant CB as heapReferenceCallback

    BFS->>BFS: pull up to budget tags from front of _pending_expand
    BFS->>JVMTI: GetObjectsWithTags, resolve which tags are still live
    JVMTI-->>BFS: live jobject references
    BFS->>JNI: EnsureLocalCapacity, then NewObjectArray to build holder
    alt exception, EnsureLocalCapacity failure, or null holder
        BFS->>BFS: ctx.truncated = true, retry this batch next pass
    else holder built successfully
        BFS->>JNI: SetObjectArrayElement per resolved object
        BFS->>JVMTI: FollowReferences, initial_object = holder
        JVMTI->>CB: heapReferenceCallback per outgoing edge
        CB-->>JVMTI: descend only for batch_tags boundary objects
        JVMTI-->>BFS: one BFS hop expanded for the whole batch
        BFS->>BFS: markExpanded, admitObject appends children to _pending_expand
    end
Loading

expandFrontier()'s JNI/JVMTI error handling is defensive by construction: a
null holder, a pending JNI exception after NewObjectArray/
SetObjectArrayElement, or an EnsureLocalCapacity failure all set
ctx.truncated = true (retry next pass) instead of marking the batch
permanently EXPANDED; a failed java/lang/Object class resolution with
pending work also forces truncated = true, so runPass() cannot mistake
it for SearchState::COMPLETED.

Termination and pacing — an unbounded traversal could otherwise stall a
GC safepoint or run forever:

stateDiagram-v2
    direction LR
    [*] --> RUNNING
    RUNNING --> COMPLETED: frontier drained,<br/>no truncation this pass
    RUNNING --> ABANDONED: frontier-size cap hit,<br/>or wall-clock TTL exceeded<br/>with work still pending
    COMPLETED --> RUNNING: restartSearch
    ABANDONED --> RUNNING: restartSearch

    note right of ABANDONED
        SearchAbandonReason (FRONTIER_CAP/TTL)
        records which cutoff fired, for the
        T_REFERENCE_CHAIN_ABANDONED JFR event
    end note
    note right of RUNNING
        restartSearch only fires once a leak
        candidate is seen and PainBudget allows it
    end note
Loading
  • Per-pass budget, hop cap, wall-clock TTL, and frontier-size cap (the last
    treated as immediate search abandonment, not per-pass truncation) —
    configured via referencechains=true:hops=N:budget=N:ttl=N:framecap=N
    (plus firstpassbudget=N for the first pass's own budget). Negative
    hops/budget/framecap values are floored (an unfloored negative
    hops would otherwise wrap to ~4e9 as a u32, silently disabling the cap
    it's meant to enforce); all three are also ceiling-clamped.
  • A PID pause-time pacing controller (updatePacing(), pausetarget=N ms)
    adapts the effective per-pass budget and cadence toward a configured
    safepoint-pause SLO.
  • Search restarts are gated on an actual leak indication existing (so a
    restarted walk is very likely to still find the object) plus a
    PainBudget cooldown (painbudget=N, 0-100) — a leaky bucket over
    cumulative safepoint time spent, so a cheap search can restart sooner than
    an expensive one. This closes a structural gap where a one-shot walk could
    finish before population-trend detection had accumulated enough GC epochs
    to flag a candidate, leaving anything allocated afterward permanently
    undiscoverable.
  • Leak-candidate selection (LivenessTracker::selectLeakCandidates()) requires
    a klass's population growth to clear both a magnitude bar and a floor-rise
    bar for LEAK_TREND_HYSTERESIS_BASE (5) consecutive qualifying epochs —
    lowered to LEAK_TREND_HYSTERESIS_CORROBORATED (3) when the aggregate
    post-GC heap floor is also rising — rather than trusting a single epoch's
    positive slope. This closes a false-positive gap where an oscillating
    ("see-saw") population could otherwise trigger a search with no real
    longer-term growth.

JFR persistence across dumps: a resolved chain is cached per klass id
(_resolved_chains, capped at 256 entries, drop-not-evict once full — see
REFERENCE_CHAIN_EVENTS_DROPPED) and re-stamped into every subsequent dump
the sample survives into (drainPendingChainEvents() snapshots without
clearing, mirroring how LivenessTracker re-emits live-object samples), so a
long-lived leak's chain is present in each JFR chunk rather than only the
chunk active when it was first reconstructed. Verified end-to-end in
ExternalProcessReferenceChainTest.

Motivation:
PROF-15341 — give live-heap samples that survive long enough to look
leak-shaped an actual referrer chain, not just "this object is still alive",
without regressing safepoint-pause behaviour on low-pause collectors.

Additional Notes:
See doc/reference-chains-design.md and doc/reference-chains-collection-summary.md
for the full design and leak-detection mechanism. Per-pass
budget/hop-cap/TTL/frontier-cap/pause-target/pain-budget defaults are round,
unbenchmarked placeholders pending a measurement pass — no JMH harness for
this feature exists yet, though utils/ has repro, parameter-sweep, and
JFR-report shell/Python tooling used to characterise pause behaviour ad hoc.
Earlier planning/proposal docs (implementation plan, remaining-work plan,
benchmark plan, and an alternative VMStructs-walk design) described approaches
or work that were superseded or shipped differently than planned, so they were
dropped from this PR (kept locally, not committed) rather than left to drift
further from the code.

How to test the change?:

  • ./gradlew :ddprof-lib:gtestDebug — native unit suite, including
    ReferenceChainsTest/ReferenceChainsBfsTest/ReferenceChainsTagTest/
    FrontierTableTest/PollWatchedTargetsTest/ResolvedChainCacheTest,
    PainBudgetTest, SearchRestartTest (restart gate),
    ReferenceChainJfrRoundtripTest (JFR encode/decode), ArgumentsTest
    (referencechains= sub-option parsing, including hops/budget/framecap
    clamping behaviour), and the LivenessTracker/SelectLeakCandidates
    bridging tests.
  • ./gradlew :ddprof-test:testDebug --tests "*ExternalProcessReferenceChainTest*"
    genuinely separate-process end-to-end test: runs a real leaking-cache Java
    app in a child JVM, asserts the reconstructed chain's leaf class, and
    confirms the chain re-emits into three fresh JFR dumps (across-dumps
    persistence).
  • ./gradlew :ddprof-test:testDebug --tests "*ReferenceChainTrackingTest*"
    in-process coverage for the walk engine, target-selection bridging, and
    abandonment reporting.

For Datadog employees:

  • If this PR touches code that signs or publishes builds or packages, or handles
    credentials of any kind, I've requested a security review (run the dd:platform-security-review
    skill, or file a request via the PSEC review form).
    bewaire also runs automatically on every PR.
  • This PR doesn't touch any of that.
  • JIRA: PROF-15341

Unsure? Have a question? Request a review!

🤖 Generated with Claude Code

@jbachorik jbachorik added the AI label Jul 8, 2026
@datadog-datadog-prod-us1

This comment has been minimized.

@dd-octo-sts

dd-octo-sts Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

CI Test Results

Run: #32263379800 | Commit: deac396 | Duration: 28m 21s (longest job)

All 32 test jobs passed

Status Overview

JDK glibc-aarch64/debug glibc-amd64/debug musl-aarch64/debug musl-amd64/debug
8 - - -
8-ibm - - -
8-j9 - -
8-librca - -
8-orcl - - -
11 - - -
11-j9 - -
11-librca - -
17 - -
17-graal - -
17-j9 - -
17-librca - -
21 - -
21-graal - -
21-librca - -
25 - -
25-graal - -
25-librca - -

Legend: ✅ passed | ❌ failed | ⚪ skipped | 🚫 cancelled

Summary: Total: 32 | Passed: 32 | Failed: 0


Updated: 2026-08-19 14:51:30 UTC

@dd-octo-sts

dd-octo-sts Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Reliability & Chaos Results

1 failure(s) detected Pipeline: https://gitlab.ddbuild.io/DataDog/java-profiler/-/pipelines/127431974

❌ chaos: profiler gmalloc aarch64 21 0 3 temXchaos
chaos.jar unavailable

@dd-octo-sts

dd-octo-sts Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (commit cfec595)

Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/123822759 Commit: cfec5956f438cc43b62cd2c81cbe46077ef66168

⚠️ Significant outliers

  • 💥 akka-uct (JDK 25): latest crashed
Runtime details (per benchmark × JDK)
Benchmark JDK Latest Dev Δ (dev vs latest) Issues L/D
akka-uct 25 💥 0 ms (0 iters) 💥 0 ms (0 iters) — / —
Internal counter details (ddprof)

ddprof internal counters, latest / dev (✅ = 0, · = unavailable):

Benchmark JDK Dropped rec Dropped jvmti Dropped trace Skipped WC AGCT fail Unwind fail
akka-uct 21 ✅ / ✅ ✅ / ✅ 3 / 1 1949 / 2055 ✅ / ✅ ✅ / ✅
akka-uct 25 ✅ / · ✅ / · ✅ / · ✅ / · ✅ / · ✅ / ·
finagle-chirper 21 ✅ / ✅ ✅ / ✅ 4 / 1 8727 / 8389 ✅ / ✅ ✅ / ✅
finagle-chirper 25 ✅ / ✅ ✅ / ✅ 1 / 1 8166 / 8396 ✅ / ✅ ✅ / ✅
fj-kmeans 21 ✅ / ✅ ✅ / ✅ 3 / 2 1292 / 1256 ✅ / ✅ ✅ / ✅
fj-kmeans 25 ✅ / ✅ ✅ / ✅ 3 / 1 1288 / 1290 ✅ / ✅ ✅ / ✅
future-genetic 21 ✅ / ✅ ✅ / ✅ 2 / 1 2830 / 3023 ✅ / ✅ ✅ / ✅
future-genetic 25 ✅ / ✅ ✅ / ✅ 2 / ✅ 2976 / 2821 ✅ / ✅ ✅ / ✅
naive-bayes 21 · / ✅ · / ✅ · / 3 · / 3555 · / ✅ · / ✅
naive-bayes 25 ✅ / ✅ ✅ / ✅ 2 / 3 3499 / 3485 ✅ / ✅ ✅ / ✅
reactors 21 ✅ / ✅ ✅ / ✅ 1 / 1 1592 / 1697 ✅ / ✅ ✅ / ✅
reactors 25 ✅ / ✅ ✅ / ✅ ✅ / 1 1922 / 1854 ✅ / ✅ ✅ / ✅

jbachorik added a commit that referenced this pull request Jul 10, 2026
…racking

Addresses review findings from PR #644 (reference chains for surviving
live-heap samples): FrontierTable's shared-lock mutation race, a
single-byte JFR event size prefix that silently truncates chains longer
than 255 bytes, blocking sample-lock retries in writeReferenceChain()
now bounded by a shared per-batch deadline with a drop counter,
unvalidated referencechains sub-option values, a startThread()/
pthread_kill() race publishing _running before the thread handle is
initialized, a null-JNIEnv leak in threadLoop(), releaseSearchTags()
now surfacing GetObjectsWithTags() failures so restartSearch() never
resets tag state prematurely, resolveLoadedClasses() skipping its
per-class scan only when the loaded-class count is unchanged (not just
non-decreasing), a spurious COMPLETED state after a failed first-pass
FollowReferences call, and removal of a leftover debug helper in
ExternalProcessReferenceChainTest. Adds regression test coverage for
release-failure and negative-value option paths.

Verified via the full ddprof-lib gtestDebug suite (149/149 tasks,
57/57 referenceChains_ut tests).

Environment: Datadog workspace

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@dd-octo-sts

dd-octo-sts Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (commit 0714bd3)

Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/124004132 Commit: 0714bd3166261f2710c22414dbfa68c11c699bdc

⚠️ Significant outliers

  • 🟢 fj-kmeans (JDK 21): runtime -2.6% (2834→2761 ms)
Runtime details (per benchmark × JDK)
Benchmark JDK Latest Dev Δ (dev vs latest) Issues L/D
akka-uct 21 ✅ 10184 ms (21 iters) ✅ 10371 ms (21 iters) ≈ +1.8% (±11.5%) — / —
akka-uct 25 ✅ 8891 ms (24 iters) ✅ 8865 ms (24 iters) ≈ -0.3% (±10.2%) — / —
finagle-chirper 21 ✅ 6003 ms (33 iters) ✅ 6034 ms (33 iters) ≈ +0.5% (±25.2%) ⚠️ W:3 / ⚠️ W:3
finagle-chirper 25 ✅ 5507 ms (36 iters) ✅ 5426 ms (36 iters) ≈ -1.5% (±23.8%) ⚠️ W:4 / ⚠️ W:3
fj-kmeans 21 ✅ 2834 ms (66 iters) ✅ 2761 ms (67 iters) 🟢 -2.6% — / —
fj-kmeans 25 ✅ 2824 ms (66 iters) ✅ 2838 ms (66 iters) ≈ +0.5% (±2.7%) — / —
future-genetic 21 ✅ 2113 ms (88 iters) ✅ 2065 ms (89 iters) ≈ -2.3% (±2.6%) — / —
future-genetic 25 ✅ 2059 ms (90 iters) ✅ 2102 ms (89 iters) ≈ +2.1% (±2.6%) — / —
naive-bayes 21 ✅ 1283 ms (134 iters) ✅ 1260 ms (135 iters) ≈ -1.8% (±32%) — / —
naive-bayes 25 ✅ 1016 ms (169 iters) ✅ 1017 ms (168 iters) ≈ +0.1% (±31.5%) — / —
reactors 21 ✅ 16658 ms (15 iters) ✅ 16371 ms (16 iters) ≈ -1.7% (±9%) — / —
reactors 25 ✅ 18507 ms (15 iters) ✅ 18056 ms (15 iters) ≈ -2.4% (±5.6%) — / —
Internal counter details (ddprof)

ddprof internal counters, latest / dev (✅ = 0, · = unavailable):

Benchmark JDK Dropped rec Dropped jvmti Dropped trace Skipped WC AGCT fail Unwind fail
akka-uct 21 ✅ / ✅ ✅ / ✅ 2 / 1 1929 / 2034 ✅ / ✅ ✅ / ✅
akka-uct 25 ✅ / ✅ ✅ / ✅ 3 / 1 2310 / 2113 ✅ / ✅ ✅ / ✅
finagle-chirper 21 ✅ / ✅ ✅ / ✅ 4 / 5 8529 / 8852 ✅ / ✅ ✅ / ✅
finagle-chirper 25 ✅ / ✅ ✅ / ✅ 2 / 2 8635 / 8390 ✅ / ✅ ✅ / ✅
fj-kmeans 21 ✅ / ✅ ✅ / ✅ 1 / 5 1284 / 1281 ✅ / ✅ ✅ / ✅
fj-kmeans 25 ✅ / ✅ ✅ / ✅ 5 / 2 1283 / 1285 ✅ / ✅ ✅ / ✅
future-genetic 21 ✅ / ✅ ✅ / ✅ 1 / 1 2930 / 2864 ✅ / ✅ ✅ / ✅
future-genetic 25 ✅ / ✅ ✅ / ✅ 2 / ✅ 2963 / 2953 ✅ / ✅ ✅ / ✅
naive-bayes 21 ✅ / ✅ ✅ / ✅ 7 / 6 3558 / 3510 ✅ / ✅ ✅ / ✅
naive-bayes 25 ✅ / ✅ ✅ / ✅ 2 / 4 3463 / 3469 ✅ / ✅ ✅ / ✅
reactors 21 ✅ / ✅ ✅ / ✅ 1 / 1 1564 / 1782 ✅ / ✅ ✅ / ✅
reactors 25 ✅ / ✅ ✅ / ✅ ✅ / ✅ 1905 / 1681 ✅ / ✅ ✅ / ✅

@dd-octo-sts

dd-octo-sts Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (commit 700d838)

Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/124275960 Commit: 700d838fc33298d5789fd9e1b29c8a142c11f41e

⚠️ Significant outliers

  • 🔴 fj-kmeans (JDK 21): runtime +4.5% (2598→2715 ms)
Runtime details (per benchmark × JDK)
Benchmark JDK Latest Dev Δ (dev vs latest) Issues L/D
akka-uct 21 ✅ 10425 ms (21 iters) ✅ 10259 ms (21 iters) ≈ -1.6% (±11.1%) — / —
akka-uct 25 ✅ 8748 ms (24 iters) ✅ 8797 ms (24 iters) ≈ +0.6% (±10.5%) — / —
finagle-chirper 21 ✅ 5970 ms (33 iters) ✅ 5982 ms (33 iters) ≈ +0.2% (±25.3%) ⚠️ W:3 / ⚠️ W:3
finagle-chirper 25 ✅ 5469 ms (36 iters) ✅ 5468 ms (36 iters) ≈ -0% (±24.3%) ⚠️ W:3 / ⚠️ W:3
fj-kmeans 21 ✅ 2598 ms (73 iters) ✅ 2715 ms (69 iters) 🔴 +4.5% — / —
fj-kmeans 25 ✅ 2784 ms (67 iters) ✅ 2847 ms (66 iters) ≈ +2.3% (±2.7%) — / —
future-genetic 21 ✅ 2122 ms (87 iters) ✅ 2126 ms (88 iters) ≈ +0.2% (±2.7%) — / —
future-genetic 25 ✅ 2035 ms (91 iters) ✅ 2057 ms (91 iters) ≈ +1.1% (±2.7%) — / —
naive-bayes 21 ✅ 1304 ms (131 iters) ✅ 1253 ms (136 iters) ≈ -3.9% (±32.2%) — / —
naive-bayes 25 ✅ 1015 ms (169 iters) ✅ 1018 ms (168 iters) ≈ +0.3% (±31.8%) — / —
reactors 21 ✅ 16107 ms (15 iters) ✅ 16847 ms (15 iters) ≈ +4.6% (±7.9%) — / —
reactors 25 ✅ 18469 ms (15 iters) ✅ 19075 ms (14 iters) ≈ +3.3% (±4.9%) — / —
Internal counter details (ddprof)

ddprof internal counters, latest / dev (✅ = 0, · = unavailable):

Benchmark JDK Dropped rec Dropped jvmti Dropped trace Skipped WC AGCT fail Unwind fail
akka-uct 21 ✅ / ✅ ✅ / ✅ 3 / ✅ 1950 / 1991 ✅ / ✅ ✅ / ✅
akka-uct 25 ✅ / ✅ ✅ / ✅ ✅ / 2 2228 / 2453 ✅ / ✅ ✅ / ✅
finagle-chirper 21 ✅ / ✅ ✅ / ✅ 6 / 2 8686 / 8417 ✅ / ✅ ✅ / ✅
finagle-chirper 25 ✅ / ✅ ✅ / ✅ 1 / 1 8561 / 8290 ✅ / ✅ ✅ / ✅
fj-kmeans 21 ✅ / ✅ ✅ / ✅ 2 / 2 1272 / 1289 ✅ / ✅ ✅ / ✅
fj-kmeans 25 ✅ / ✅ ✅ / ✅ 2 / ✅ 1287 / 1298 ✅ / ✅ ✅ / ✅
future-genetic 21 ✅ / ✅ ✅ / ✅ 1 / ✅ 2912 / 3017 ✅ / ✅ ✅ / ✅
future-genetic 25 ✅ / ✅ ✅ / ✅ ✅ / 1 2907 / 2872 ✅ / ✅ ✅ / ✅
naive-bayes 21 ✅ / ✅ ✅ / ✅ 2 / 3 3515 / 3489 ✅ / ✅ ✅ / ✅
naive-bayes 25 ✅ / ✅ ✅ / ✅ 2 / 7 3498 / 3463 ✅ / ✅ ✅ / ✅
reactors 21 ✅ / ✅ ✅ / ✅ 2 / 2 1732 / 1701 ✅ / ✅ ✅ / ✅
reactors 25 ✅ / ✅ ✅ / ✅ ✅ / ✅ 1780 / 1924 ✅ / ✅ ✅ / ✅

jbachorik added a commit that referenced this pull request Jul 14, 2026
…racking

Addresses review findings from PR #644 (reference chains for surviving
live-heap samples): FrontierTable's shared-lock mutation race, a
single-byte JFR event size prefix that silently truncates chains longer
than 255 bytes, blocking sample-lock retries in writeReferenceChain()
now bounded by a shared per-batch deadline with a drop counter,
unvalidated referencechains sub-option values, a startThread()/
pthread_kill() race publishing _running before the thread handle is
initialized, a null-JNIEnv leak in threadLoop(), releaseSearchTags()
now surfacing GetObjectsWithTags() failures so restartSearch() never
resets tag state prematurely, resolveLoadedClasses() skipping its
per-class scan only when the loaded-class count is unchanged (not just
non-decreasing), a spurious COMPLETED state after a failed first-pass
FollowReferences call, and removal of a leftover debug helper in
ExternalProcessReferenceChainTest. Adds regression test coverage for
release-failure and negative-value option paths.

Verified via the full ddprof-lib gtestDebug suite (149/149 tasks,
57/57 referenceChains_ut tests).

Environment: Datadog workspace

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@jbachorik
jbachorik force-pushed the jb/reference-chains branch from 700d838 to 9972bff Compare July 14, 2026 12:03
@dd-octo-sts

dd-octo-sts Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (commit 9972bff)

Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/124511782 Commit: 9972bff9464d4d2a8ee4a279dc8f8d35c88dd2e9

✅ Within expected boundaries

No significant runtime deltas (all within run-to-run noise) and no internal-counter outliers.

Runtime details (per benchmark × JDK)
Benchmark JDK Latest Dev Δ (dev vs latest) Issues L/D
akka-uct 21 ✅ 10365 ms (21 iters) ✅ 10456 ms (21 iters) ≈ +0.9% (±12%) — / —
akka-uct 25 ✅ 8820 ms (24 iters) ✅ 8890 ms (24 iters) ≈ +0.8% (±9.9%) — / —
finagle-chirper 21 ✅ 5990 ms (33 iters) ✅ 6050 ms (33 iters) ≈ +1% (±24.8%) ⚠️ W:3 / ⚠️ W:3
finagle-chirper 25 ✅ 5495 ms (36 iters) ✅ 5462 ms (36 iters) ≈ -0.6% (±24.2%) ⚠️ W:3 / ⚠️ W:3
fj-kmeans 21 ✅ 2745 ms (68 iters) ✅ 2714 ms (69 iters) ≈ -1.1% (±2.7%) — / —
fj-kmeans 25 ✅ 2831 ms (66 iters) ✅ 2828 ms (66 iters) ≈ -0.1% (±2.6%) — / —
future-genetic 21 ✅ 2150 ms (87 iters) ✅ 2112 ms (87 iters) ≈ -1.8% (±2.7%) — / —
future-genetic 25 ✅ 2073 ms (90 iters) ✅ 2121 ms (87 iters) ≈ +2.3% (±2.5%) — / —
naive-bayes 21 ✅ 1287 ms (133 iters) ✅ 1312 ms (131 iters) ≈ +1.9% (±33%) — / —
naive-bayes 25 ✅ 1017 ms (168 iters) ✅ 1030 ms (166 iters) ≈ +1.3% (±32.1%) — / —
reactors 21 ✅ 15980 ms (15 iters) ✅ 16718 ms (15 iters) ≈ +4.6% (±7%) — / —
reactors 25 ✅ 18712 ms (15 iters) ✅ 18346 ms (15 iters) ≈ -2% (±3.1%) — / —
Internal counter details (ddprof)

ddprof internal counters, latest / dev (✅ = 0, · = unavailable):

Benchmark JDK Dropped rec Dropped jvmti Dropped trace Skipped WC AGCT fail Unwind fail
akka-uct 21 ✅ / ✅ ✅ / ✅ 1 / 1 1902 / 2028 ✅ / ✅ ✅ / ✅
akka-uct 25 ✅ / ✅ ✅ / ✅ 2 / 1 2103 / 2152 ✅ / ✅ ✅ / ✅
finagle-chirper 21 ✅ / ✅ ✅ / ✅ 3 / 5 8628 / 8249 ✅ / ✅ ✅ / ✅
finagle-chirper 25 ✅ / ✅ ✅ / ✅ 1 / 2 8833 / 8700 ✅ / ✅ ✅ / ✅
fj-kmeans 21 ✅ / ✅ ✅ / ✅ 1 / 3 1256 / 1240 ✅ / ✅ ✅ / ✅
fj-kmeans 25 ✅ / ✅ ✅ / ✅ 4 / 5 1264 / 1305 ✅ / ✅ ✅ / ✅
future-genetic 21 ✅ / ✅ ✅ / ✅ ✅ / 2 3046 / 3002 ✅ / ✅ ✅ / ✅
future-genetic 25 ✅ / ✅ ✅ / ✅ ✅ / 3 2879 / 2830 ✅ / ✅ ✅ / ✅
naive-bayes 21 ✅ / ✅ ✅ / ✅ ✅ / 2 3479 / 3463 ✅ / ✅ ✅ / ✅
naive-bayes 25 ✅ / ✅ ✅ / ✅ 1 / 5 3476 / 3482 ✅ / ✅ ✅ / ✅
reactors 21 ✅ / ✅ ✅ / ✅ ✅ / ✅ ✅ / ✅ ✅ / ✅ ✅ / ✅
reactors 25 ✅ / ✅ ✅ / ✅ ✅ / 1 1862 / 1833 ✅ / ✅ ✅ / ✅

@dd-octo-sts

dd-octo-sts Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (commit a636398)

Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/124700626 Commit: a636398f93c3fd15fb9fd3b6253da66f36e56742

✅ Within expected boundaries

No significant runtime deltas (all within run-to-run noise) and no internal-counter outliers.

Runtime details (per benchmark × JDK)
Benchmark JDK Latest Dev Δ (dev vs latest) Issues L/D
akka-uct 21 ✅ 10127 ms (21 iters) ✅ 10208 ms (21 iters) ≈ +0.8% (±11.6%) — / —
akka-uct 25 ✅ 9010 ms (24 iters) ✅ 8842 ms (24 iters) ≈ -1.9% (±10.3%) — / —
finagle-chirper 21 ✅ 6004 ms (33 iters) ✅ 5987 ms (33 iters) ≈ -0.3% (±25%) ⚠️ W:3 / ⚠️ W:3
finagle-chirper 25 ✅ 5500 ms (36 iters) ✅ 5500 ms (36 iters) ≈ 0% (±24.6%) ⚠️ W:3 / ⚠️ W:3
fj-kmeans 21 ✅ 2714 ms (69 iters) ✅ 2715 ms (68 iters) ≈ +0% (±2.7%) — / —
fj-kmeans 25 ✅ 2812 ms (66 iters) ✅ 2837 ms (66 iters) ≈ +0.9% (±2.6%) — / —
future-genetic 21 ✅ 2107 ms (88 iters) ✅ 2065 ms (90 iters) ≈ -2% (±2.6%) — / —
future-genetic 25 ✅ 2018 ms (92 iters) ✅ 2025 ms (92 iters) ≈ +0.3% (±2.8%) — / —
naive-bayes 21 ✅ 1246 ms (137 iters) ✅ 1227 ms (139 iters) ≈ -1.5% (±32.3%) — / —
naive-bayes 25 ✅ 1003 ms (170 iters) ✅ 1028 ms (166 iters) ≈ +2.5% (±32%) — / —
reactors 21 ✅ 15862 ms (15 iters) ✅ 16456 ms (15 iters) ≈ +3.7% (±9.3%) — / —
reactors 25 ✅ 18376 ms (15 iters) 💥 0 ms (0 iters) — / —
Internal counter details (ddprof)

ddprof internal counters, latest / dev (✅ = 0, · = unavailable):

Benchmark JDK Dropped rec Dropped jvmti Dropped trace Skipped WC AGCT fail Unwind fail
akka-uct 21 ✅ / ✅ ✅ / ✅ 3 / 4 1972 / 1963 ✅ / ✅ ✅ / ✅
akka-uct 25 ✅ / ✅ ✅ / ✅ 2 / 6 2396 / 2157 ✅ / ✅ ✅ / ✅
finagle-chirper 21 ✅ / ✅ ✅ / ✅ 2 / 2 8557 / 8411 ✅ / ✅ ✅ / ✅
finagle-chirper 25 ✅ / ✅ ✅ / ✅ 4 / ✅ 8575 / 8705 ✅ / ✅ ✅ / ✅
fj-kmeans 21 ✅ / ✅ ✅ / ✅ 1 / ✅ 1288 / 1228 ✅ / ✅ ✅ / ✅
fj-kmeans 25 ✅ / ✅ ✅ / ✅ 1 / 3 1268 / 1277 ✅ / ✅ ✅ / ✅
future-genetic 21 ✅ / ✅ ✅ / ✅ 2 / 2 2972 / 2870 ✅ / ✅ ✅ / ✅
future-genetic 25 ✅ / ✅ ✅ / ✅ 1 / 2 2818 / 2994 ✅ / ✅ ✅ / ✅
naive-bayes 21 ✅ / ✅ ✅ / ✅ 3 / 2 3519 / 3503 ✅ / ✅ ✅ / ✅
naive-bayes 25 ✅ / ✅ ✅ / ✅ 2 / 3 3468 / 3470 ✅ / ✅ ✅ / ✅
reactors 21 ✅ / ✅ ✅ / ✅ ✅ / 1 1567 / 1727 ✅ / ✅ ✅ / ✅
reactors 25 ✅ / ✅ ✅ / ✅ ✅ / ✅ 1929 / 1873 ✅ / ✅ ✅ / ✅

@dd-octo-sts

dd-octo-sts Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (commit f2d8978)

Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/124747648 Commit: f2d8978c23b568eb4ce8006e8e9b901cdaed11c3

✅ Within expected boundaries

No significant runtime deltas (all within run-to-run noise) and no internal-counter outliers.

Runtime details (per benchmark × JDK)
Benchmark JDK Latest Dev Δ (dev vs latest) Issues L/D
akka-uct 21 ✅ 10331 ms (21 iters) ✅ 10246 ms (21 iters) ≈ -0.8% (±11.2%) — / —
akka-uct 25 ✅ 8728 ms (24 iters) ✅ 8956 ms (24 iters) ≈ +2.6% (±10.6%) — / —
finagle-chirper 21 ✅ 6011 ms (33 iters) ✅ 5985 ms (33 iters) ≈ -0.4% (±25.6%) ⚠️ W:3 / ⚠️ W:3
finagle-chirper 25 ✅ 5522 ms (36 iters) ✅ 5441 ms (36 iters) ≈ -1.5% (±24.4%) ⚠️ W:3 / ⚠️ W:3
fj-kmeans 21 ✅ 2699 ms (70 iters) ✅ 2742 ms (68 iters) ≈ +1.6% (±2.7%) — / —
fj-kmeans 25 ✅ 2806 ms (66 iters) ✅ 2796 ms (66 iters) ≈ -0.4% (±2.6%) — / —
future-genetic 25 ✅ 2082 ms (89 iters) ✅ 2051 ms (90 iters) ≈ -1.5% (±2.6%) — / —
naive-bayes 21 ✅ 1252 ms (136 iters) ✅ 1252 ms (136 iters) ≈ 0% (±32.7%) — / —
reactors 21 ✅ 16483 ms (15 iters) ✅ 17081 ms (15 iters) ≈ +3.6% (±7.9%) — / —
Internal counter details (ddprof)

ddprof internal counters, latest / dev (✅ = 0, · = unavailable):

Benchmark JDK Dropped rec Dropped jvmti Dropped trace Skipped WC AGCT fail Unwind fail
akka-uct 21 ✅ / ✅ ✅ / ✅ 1 / 4 1998 / 1935 ✅ / ✅ ✅ / ✅
akka-uct 25 ✅ / ✅ ✅ / ✅ 3 / 2 2240 / 2356 ✅ / ✅ ✅ / ✅
finagle-chirper 21 ✅ / ✅ ✅ / ✅ 3 / 5 8506 / 8471 ✅ / ✅ ✅ / ✅
finagle-chirper 25 ✅ / ✅ ✅ / ✅ 1 / 1 8384 / 7986 ✅ / ✅ ✅ / ✅
fj-kmeans 21 ✅ / ✅ ✅ / ✅ 5 / 3 1293 / 1281 ✅ / ✅ ✅ / ✅
fj-kmeans 25 ✅ / ✅ ✅ / ✅ 2 / 2 1255 / 1280 ✅ / ✅ ✅ / ✅
future-genetic 21 ✅ / ✅ ✅ / ✅ ✅ / 2 2987 / 2963 ✅ / ✅ ✅ / ✅
future-genetic 25 ✅ / ✅ ✅ / ✅ ✅ / 2 2916 / 2851 ✅ / ✅ ✅ / ✅
naive-bayes 21 ✅ / ✅ ✅ / ✅ 5 / 4 3501 / 3519 ✅ / ✅ ✅ / ✅
naive-bayes 25 ✅ / ✅ ✅ / ✅ 6 / 9 3486 / 3432 ✅ / ✅ ✅ / ✅
reactors 21 ✅ / ✅ ✅ / ✅ 1 / 1 1617 / 1818 ✅ / ✅ ✅ / ✅
reactors 25 ✅ / ✅ ✅ / ✅ ✅ / 1 1931 / 1868 ✅ / ✅ ✅ / ✅

@dd-octo-sts

dd-octo-sts Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (commit a48899b)

Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/124773626 Commit: a48899b982ca5139081f665d6ffe1b8b5c3790fa

⚠️ Significant outliers

  • 🔴 fj-kmeans (JDK 21): runtime +2.8% (2687→2761 ms)
  • 🔴 future-genetic (JDK 21): runtime +3.4% (2047→2116 ms)
  • 🔴 future-genetic (JDK 25): runtime +4.2% (2000→2083 ms)
Runtime details (per benchmark × JDK)
Benchmark JDK Latest Dev Δ (dev vs latest) Issues L/D
akka-uct 21 ✅ 10376 ms (21 iters) ✅ 10228 ms (21 iters) ≈ -1.4% (±11%) — / —
akka-uct 25 ✅ 8829 ms (24 iters) ✅ 8859 ms (24 iters) ≈ +0.3% (±10%) — / —
finagle-chirper 21 ✅ 6001 ms (33 iters) ✅ 5928 ms (33 iters) ≈ -1.2% (±25.2%) ⚠️ W:3 / ⚠️ W:3
finagle-chirper 25 ✅ 5469 ms (36 iters) ✅ 5475 ms (36 iters) ≈ +0.1% (±24.5%) ⚠️ W:3 / ⚠️ W:3
fj-kmeans 21 ✅ 2687 ms (70 iters) ✅ 2761 ms (68 iters) 🔴 +2.8% — / —
fj-kmeans 25 ✅ 2810 ms (66 iters) ✅ 2805 ms (66 iters) ≈ -0.2% (±2.6%) — / —
future-genetic 21 ✅ 2047 ms (91 iters) ✅ 2116 ms (88 iters) 🔴 +3.4% — / —
future-genetic 25 ✅ 2000 ms (93 iters) ✅ 2083 ms (89 iters) 🔴 +4.2% — / —
naive-bayes 21 ✅ 1256 ms (136 iters) ✅ 1253 ms (136 iters) ≈ -0.2% (±32.8%) — / —
naive-bayes 25 ✅ 1016 ms (169 iters) ✅ 1016 ms (169 iters) ≈ 0% (±31.3%) — / —
reactors 21 ✅ 16870 ms (15 iters) ✅ 16463 ms (15 iters) ≈ -2.4% (±7.3%) — / —
reactors 25 ✅ 18270 ms (15 iters) ✅ 18333 ms (15 iters) ≈ +0.3% (±4.5%) — / —
Internal counter details (ddprof)

ddprof internal counters, latest / dev (✅ = 0, · = unavailable):

Benchmark JDK Dropped rec Dropped jvmti Dropped trace Skipped WC AGCT fail Unwind fail
akka-uct 21 ✅ / ✅ ✅ / ✅ 4 / 2 1988 / 2024 ✅ / ✅ ✅ / ✅
akka-uct 25 ✅ / ✅ ✅ / ✅ 2 / 3 2263 / 2395 ✅ / ✅ ✅ / ✅
finagle-chirper 21 ✅ / ✅ ✅ / ✅ 1 / 6 8440 / 8679 ✅ / ✅ ✅ / ✅
finagle-chirper 25 ✅ / ✅ ✅ / ✅ ✅ / 2 8283 / 8410 ✅ / ✅ ✅ / ✅
fj-kmeans 21 ✅ / ✅ ✅ / ✅ 2 / 1 1288 / 1260 ✅ / ✅ ✅ / ✅
fj-kmeans 25 ✅ / ✅ ✅ / ✅ 4 / 4 1250 / 1295 ✅ / ✅ ✅ / ✅
future-genetic 21 ✅ / ✅ ✅ / ✅ 3 / 2 2968 / 3003 ✅ / ✅ ✅ / ✅
future-genetic 25 ✅ / ✅ ✅ / ✅ ✅ / 1 2843 / 2902 ✅ / ✅ ✅ / ✅
naive-bayes 21 ✅ / ✅ ✅ / ✅ 4 / 5 3474 / 3510 ✅ / ✅ ✅ / ✅
naive-bayes 25 ✅ / ✅ ✅ / ✅ 3 / 2 3495 / 3488 ✅ / ✅ ✅ / ✅
reactors 21 ✅ / ✅ ✅ / ✅ ✅ / ✅ 1726 / 1721 ✅ / ✅ ✅ / ✅
reactors 25 ✅ / ✅ ✅ / ✅ ✅ / 3 1893 / 1750 ✅ / ✅ ✅ / ✅

@dd-octo-sts

dd-octo-sts Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (commit 787f29e)

Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/124811024 Commit: 787f29e84914d8da397ed2a644c91d59a2f1e08e

⚠️ Significant outliers

  • 🔴 fj-kmeans (JDK 21): runtime +5.3% (2693→2836 ms)
Runtime details (per benchmark × JDK)
Benchmark JDK Latest Dev Δ (dev vs latest) Issues L/D
akka-uct 21 ✅ 10215 ms (21 iters) ✅ 10475 ms (21 iters) ≈ +2.5% (±11.9%) — / —
akka-uct 25 ✅ 8843 ms (24 iters) ✅ 8861 ms (24 iters) ≈ +0.2% (±10.5%) — / —
finagle-chirper 21 ✅ 5986 ms (33 iters) ✅ 5971 ms (33 iters) ≈ -0.3% (±25.5%) ⚠️ W:3 / ⚠️ W:3
finagle-chirper 25 ✅ 5515 ms (36 iters) ✅ 5479 ms (36 iters) ≈ -0.7% (±24.2%) ⚠️ W:3 / ⚠️ W:4
fj-kmeans 21 ✅ 2693 ms (70 iters) ✅ 2836 ms (66 iters) 🔴 +5.3% — / —
fj-kmeans 25 ✅ 2820 ms (66 iters) ✅ 2837 ms (66 iters) ≈ +0.6% (±2.6%) — / —
future-genetic 21 ✅ 2059 ms (90 iters) ✅ 2070 ms (89 iters) ≈ +0.5% (±2.6%) — / —
future-genetic 25 ✅ 2089 ms (89 iters) ✅ 2130 ms (87 iters) ≈ +2% (±2.6%) — / —
naive-bayes 21 ✅ 1284 ms (133 iters) ✅ 1313 ms (131 iters) ≈ +2.3% (±33%) — / —
naive-bayes 25 ✅ 1009 ms (170 iters) ✅ 988 ms (173 iters) ≈ -2.1% (±31.4%) — / —
reactors 21 ✅ 16540 ms (15 iters) ✅ 16155 ms (15 iters) ≈ -2.3% (±7.1%) — / —
reactors 25 ✅ 18545 ms (15 iters) ✅ 18816 ms (15 iters) ≈ +1.5% (±4.2%) — / —
Internal counter details (ddprof)

ddprof internal counters, latest / dev (✅ = 0, · = unavailable):

Benchmark JDK Dropped rec Dropped jvmti Dropped trace Skipped WC AGCT fail Unwind fail
akka-uct 21 ✅ / ✅ ✅ / ✅ 3 / 2 1987 / 1990 ✅ / ✅ ✅ / ✅
akka-uct 25 ✅ / ✅ ✅ / ✅ 1 / 4 2238 / 2323 ✅ / ✅ ✅ / ✅
finagle-chirper 21 ✅ / ✅ ✅ / ✅ 2 / 2 8290 / 8529 ✅ / ✅ ✅ / ✅
finagle-chirper 25 ✅ / ✅ ✅ / ✅ ✅ / 1 8323 / 8386 ✅ / ✅ ✅ / ✅
fj-kmeans 21 ✅ / ✅ ✅ / ✅ 3 / 1 1299 / 1277 ✅ / ✅ ✅ / ✅
fj-kmeans 25 ✅ / ✅ ✅ / ✅ ✅ / ✅ 1274 / 1299 ✅ / ✅ ✅ / ✅
future-genetic 21 ✅ / ✅ ✅ / ✅ 2 / ✅ 2920 / 2982 ✅ / ✅ ✅ / ✅
future-genetic 25 ✅ / ✅ ✅ / ✅ 2 / 4 2886 / 2864 ✅ / ✅ ✅ / ✅
naive-bayes 21 ✅ / ✅ ✅ / ✅ ✅ / ✅ ✅ / ✅ ✅ / ✅ ✅ / ✅
naive-bayes 25 ✅ / ✅ ✅ / ✅ 4 / 1 3505 / 3499 ✅ / ✅ ✅ / ✅
reactors 21 ✅ / ✅ ✅ / ✅ ✅ / ✅ 1593 / 1669 ✅ / ✅ ✅ / ✅
reactors 25 ✅ / ✅ ✅ / ✅ 1 / 1 1849 / 1940 ✅ / ✅ ✅ / ✅

@dd-octo-sts

dd-octo-sts Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (commit fede524)

Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/124833099 Commit: fede5244169eddd7f6280cd0a9fe39b618080bd7

✅ Within expected boundaries

No significant runtime deltas (all within run-to-run noise) and no internal-counter outliers.

Runtime details (per benchmark × JDK)
Benchmark JDK Latest Dev Δ (dev vs latest) Issues L/D
akka-uct 21 ✅ 10349 ms (21 iters) ✅ 10262 ms (21 iters) ≈ -0.8% (±12.1%) — / —
akka-uct 25 ✅ 8820 ms (24 iters) ✅ 8733 ms (24 iters) ≈ -1% (±10.2%) — / —
finagle-chirper 21 ✅ 6023 ms (33 iters) ✅ 6016 ms (33 iters) ≈ -0.1% (±25.6%) ⚠️ W:4 / ⚠️ W:3
finagle-chirper 25 ✅ 5517 ms (36 iters) ✅ 5419 ms (36 iters) ≈ -1.8% (±24.1%) ⚠️ W:4 / ⚠️ W:3
fj-kmeans 21 ✅ 2713 ms (69 iters) ✅ 2649 ms (71 iters) ≈ -2.4% (±2.5%) — / —
fj-kmeans 25 ✅ 2826 ms (66 iters) ✅ 2808 ms (66 iters) ≈ -0.6% (±2.5%) — / —
future-genetic 21 ✅ 2076 ms (89 iters) ✅ 2092 ms (89 iters) ≈ +0.8% (±2.7%) — / —
future-genetic 25 ✅ 2029 ms (92 iters) ✅ 2024 ms (91 iters) ≈ -0.2% (±2.7%) — / —
naive-bayes 21 ✅ 1253 ms (136 iters) ✅ 1279 ms (134 iters) ≈ +2.1% (±33%) — / —
naive-bayes 25 ✅ 1008 ms (170 iters) ✅ 1012 ms (169 iters) ≈ +0.4% (±31.6%) — / —
reactors 21 ✅ 16287 ms (15 iters) ✅ 16063 ms (16 iters) ≈ -1.4% (±6.5%) — / —
reactors 25 ✅ 18328 ms (15 iters) ✅ 18575 ms (15 iters) ≈ +1.3% (±4.4%) — / —
Internal counter details (ddprof)

ddprof internal counters, latest / dev (✅ = 0, · = unavailable):

Benchmark JDK Dropped rec Dropped jvmti Dropped trace Skipped WC AGCT fail Unwind fail
akka-uct 21 ✅ / ✅ ✅ / ✅ 2 / 2 1913 / 1895 ✅ / ✅ ✅ / ✅
akka-uct 25 ✅ / ✅ ✅ / ✅ 2 / 3 2116 / 2231 ✅ / ✅ ✅ / ✅
finagle-chirper 21 ✅ / ✅ ✅ / ✅ 4 / 4 8775 / 8877 ✅ / ✅ ✅ / ✅
finagle-chirper 25 ✅ / ✅ ✅ / ✅ 2 / 1 8359 / 8415 ✅ / ✅ ✅ / ✅
fj-kmeans 21 ✅ / ✅ ✅ / ✅ 2 / 2 1260 / 1279 ✅ / ✅ ✅ / ✅
fj-kmeans 25 ✅ / ✅ ✅ / ✅ 2 / ✅ 1275 / 1273 ✅ / ✅ ✅ / ✅
future-genetic 21 ✅ / ✅ ✅ / ✅ ✅ / ✅ ✅ / ✅ ✅ / ✅ ✅ / ✅
future-genetic 25 ✅ / ✅ ✅ / ✅ 1 / 1 2874 / 2807 ✅ / ✅ ✅ / ✅
naive-bayes 21 ✅ / ✅ ✅ / ✅ 5 / ✅ 3520 / 3527 ✅ / ✅ ✅ / ✅
naive-bayes 25 ✅ / ✅ ✅ / ✅ 5 / 5 3466 / 3469 ✅ / ✅ ✅ / ✅
reactors 21 ✅ / ✅ ✅ / ✅ ✅ / 1 1547 / 1755 ✅ / ✅ ✅ / ✅
reactors 25 ✅ / ✅ ✅ / ✅ ✅ / 1 1873 / 1890 ✅ / ✅ ✅ / ✅

@dd-octo-sts

dd-octo-sts Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (commit ef0f5c6)

Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/124961539 Commit: ef0f5c6cd6793c440715d8f51a20218afa8cd188

✅ Within expected boundaries

No significant runtime deltas (all within run-to-run noise) and no internal-counter outliers.

Runtime details (per benchmark × JDK)
Benchmark JDK Latest Dev Δ (dev vs latest) Issues L/D
akka-uct 21 ✅ 10321 ms (21 iters) ✅ 10247 ms (21 iters) ≈ -0.7% (±11.2%) — / —
akka-uct 25 ✅ 8827 ms (24 iters) ✅ 8856 ms (24 iters) ≈ +0.3% (±9.9%) — / —
finagle-chirper 21 ✅ 5961 ms (33 iters) ✅ 5961 ms (33 iters) ≈ 0% (±25.2%) ⚠️ W:3 / ⚠️ W:3
finagle-chirper 25 ✅ 5473 ms (36 iters) ✅ 5482 ms (36 iters) ≈ +0.2% (±24.2%) ⚠️ W:3 / ⚠️ W:4
fj-kmeans 21 ✅ 2721 ms (69 iters) ✅ 2776 ms (67 iters) ≈ +2% (±2.8%) — / —
fj-kmeans 25 ✅ 2805 ms (66 iters) ✅ 2801 ms (66 iters) ≈ -0.1% (±2.6%) — / —
future-genetic 21 ✅ 2074 ms (89 iters) ✅ 2054 ms (90 iters) ≈ -1% (±2.5%) — / —
future-genetic 25 ✅ 2082 ms (89 iters) ✅ 2065 ms (90 iters) ≈ -0.8% (±2.6%) — / —
naive-bayes 21 ✅ 1255 ms (137 iters) ✅ 1266 ms (135 iters) ≈ +0.9% (±32.6%) — / —
naive-bayes 25 ✅ 1006 ms (170 iters) ✅ 979 ms (174 iters) ≈ -2.7% (±31.6%) — / —
reactors 21 ✅ 16850 ms (15 iters) ✅ 16138 ms (15 iters) ≈ -4.2% (±7%) — / —
reactors 25 ✅ 18309 ms (15 iters) ✅ 18522 ms (15 iters) ≈ +1.2% (±5.7%) — / —
Internal counter details (ddprof)

ddprof internal counters, latest / dev (✅ = 0, · = unavailable):

Benchmark JDK Dropped rec Dropped jvmti Dropped trace Skipped WC AGCT fail Unwind fail
akka-uct 21 ✅ / ✅ ✅ / ✅ 1 / 1 1878 / 1946 ✅ / ✅ ✅ / ✅
akka-uct 25 ✅ / ✅ ✅ / ✅ ✅ / 3 2374 / 2471 ✅ / ✅ ✅ / ✅
finagle-chirper 21 ✅ / ✅ ✅ / ✅ 3 / 3 8174 / 8713 ✅ / ✅ ✅ / ✅
finagle-chirper 25 ✅ / ✅ ✅ / ✅ 6 / 2 8354 / 8759 ✅ / ✅ ✅ / ✅
fj-kmeans 21 ✅ / ✅ ✅ / ✅ ✅ / 2 1279 / 1258 ✅ / ✅ ✅ / ✅
fj-kmeans 25 ✅ / ✅ ✅ / ✅ 2 / 2 1264 / 1263 ✅ / ✅ ✅ / ✅
future-genetic 21 ✅ / ✅ ✅ / ✅ 1 / 3 2985 / 2919 ✅ / ✅ ✅ / ✅
future-genetic 25 ✅ / ✅ ✅ / ✅ 2 / 5 2902 / 2849 ✅ / ✅ ✅ / ✅
naive-bayes 21 ✅ / ✅ ✅ / ✅ 4 / 5 3582 / 3490 ✅ / ✅ ✅ / ✅
naive-bayes 25 ✅ / ✅ ✅ / ✅ 9 / 3 3490 / 3517 ✅ / ✅ ✅ / ✅
reactors 21 ✅ / ✅ ✅ / ✅ 2 / 1 1749 / 1686 ✅ / ✅ ✅ / ✅
reactors 25 ✅ / ✅ ✅ / ✅ 6 / 2 1866 / 1903 ✅ / ✅ ✅ / ✅

@dd-octo-sts

dd-octo-sts Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (commit 1299316)

Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/124967829 Commit: 1299316723d6feb8baf7e573b7925f816b772b79

⚠️ Significant outliers

  • 🔴 fj-kmeans (JDK 21): runtime +4.7% (2696→2822 ms)
  • 🟢 fj-kmeans (JDK 25): runtime -3.4% (2842→2746 ms)
  • 🟢 future-genetic (JDK 25): runtime -3.5% (2134→2059 ms)
Runtime details (per benchmark × JDK)
Benchmark JDK Latest Dev Δ (dev vs latest) Issues L/D
akka-uct 21 ✅ 10336 ms (21 iters) ✅ 10245 ms (21 iters) ≈ -0.9% (±11.5%) — / —
akka-uct 25 ✅ 8868 ms (24 iters) ✅ 8789 ms (24 iters) ≈ -0.9% (±10.7%) — / —
finagle-chirper 21 ✅ 5968 ms (33 iters) ✅ 5971 ms (33 iters) ≈ +0.1% (±25.8%) ⚠️ W:3 / ⚠️ W:3
finagle-chirper 25 ✅ 5510 ms (36 iters) ✅ 5537 ms (36 iters) ≈ +0.5% (±24.5%) ⚠️ W:3 / ⚠️ W:3
fj-kmeans 21 ✅ 2696 ms (69 iters) ✅ 2822 ms (66 iters) 🔴 +4.7% — / —
fj-kmeans 25 ✅ 2842 ms (66 iters) ✅ 2746 ms (68 iters) 🟢 -3.4% — / —
future-genetic 21 ✅ 2068 ms (90 iters) ✅ 2051 ms (90 iters) ≈ -0.8% (±2.6%) — / —
future-genetic 25 ✅ 2134 ms (87 iters) ✅ 2059 ms (90 iters) 🟢 -3.5% — / —
naive-bayes 21 ✅ 1318 ms (130 iters) ✅ 1253 ms (136 iters) ≈ -4.9% (±31.8%) — / —
naive-bayes 25 ✅ 998 ms (171 iters) ✅ 1025 ms (167 iters) ≈ +2.7% (±32.1%) — / —
reactors 21 ✅ 16264 ms (15 iters) ✅ 16625 ms (15 iters) ≈ +2.2% (±7.4%) — / —
reactors 25 ✅ 17926 ms (15 iters) ✅ 18917 ms (15 iters) ≈ +5.5% (±5.6%) — / —
Internal counter details (ddprof)

ddprof internal counters, latest / dev (✅ = 0, · = unavailable):

Benchmark JDK Dropped rec Dropped jvmti Dropped trace Skipped WC AGCT fail Unwind fail
akka-uct 21 ✅ / ✅ ✅ / ✅ ✅ / 1 1917 / 1934 ✅ / ✅ ✅ / ✅
akka-uct 25 ✅ / ✅ ✅ / ✅ 3 / 3 2285 / 2434 ✅ / ✅ ✅ / ✅
finagle-chirper 21 ✅ / ✅ ✅ / ✅ 6 / 2 8503 / 8628 ✅ / ✅ ✅ / ✅
finagle-chirper 25 ✅ / ✅ ✅ / ✅ 2 / 2 8331 / 8664 ✅ / ✅ ✅ / ✅
fj-kmeans 21 ✅ / ✅ ✅ / ✅ 2 / 1 1287 / 1266 ✅ / ✅ ✅ / ✅
fj-kmeans 25 ✅ / ✅ ✅ / ✅ 2 / 2 1277 / 1290 ✅ / ✅ ✅ / ✅
future-genetic 21 ✅ / ✅ ✅ / ✅ 1 / 3 3037 / 2981 ✅ / ✅ ✅ / ✅
future-genetic 25 ✅ / ✅ ✅ / ✅ 1 / ✅ 2897 / 2943 ✅ / ✅ ✅ / ✅
naive-bayes 21 ✅ / ✅ ✅ / ✅ 3 / 12 3507 / 3493 ✅ / ✅ ✅ / ✅
naive-bayes 25 ✅ / ✅ ✅ / ✅ 5 / 5 3446 / 3481 ✅ / ✅ ✅ / ✅
reactors 21 ✅ / ✅ ✅ / ✅ 4 / ✅ 1765 / 1665 ✅ / ✅ ✅ / ✅
reactors 25 ✅ / ✅ ✅ / ✅ 1 / ✅ 1855 / 1920 ✅ / ✅ ✅ / ✅

jbachorik added a commit that referenced this pull request Jul 16, 2026
…racking

Addresses review findings from PR #644 (reference chains for surviving
live-heap samples): FrontierTable's shared-lock mutation race, a
single-byte JFR event size prefix that silently truncates chains longer
than 255 bytes, blocking sample-lock retries in writeReferenceChain()
now bounded by a shared per-batch deadline with a drop counter,
unvalidated referencechains sub-option values, a startThread()/
pthread_kill() race publishing _running before the thread handle is
initialized, a null-JNIEnv leak in threadLoop(), releaseSearchTags()
now surfacing GetObjectsWithTags() failures so restartSearch() never
resets tag state prematurely, resolveLoadedClasses() skipping its
per-class scan only when the loaded-class count is unchanged (not just
non-decreasing), a spurious COMPLETED state after a failed first-pass
FollowReferences call, and removal of a leftover debug helper in
ExternalProcessReferenceChainTest. Adds regression test coverage for
release-failure and negative-value option paths.

Verified via the full ddprof-lib gtestDebug suite (149/149 tasks,
57/57 referenceChains_ut tests).

Environment: Datadog workspace

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@jbachorik
jbachorik force-pushed the jb/reference-chains branch from 1299316 to baba070 Compare July 16, 2026 11:31
@dd-octo-sts

dd-octo-sts Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (commit baba070)

Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/124983919 Commit: baba0702198bb700cacfe9ab28c900eaf7d3f729

✅ Within expected boundaries

No significant runtime deltas (all within run-to-run noise) and no internal-counter outliers.

Runtime details (per benchmark × JDK)
Benchmark JDK Latest Dev Δ (dev vs latest) Issues L/D
akka-uct 21 ✅ 10403 ms (21 iters) ✅ 10169 ms (21 iters) ≈ -2.2% (±10.2%) — / —
akka-uct 25 ✅ 8907 ms (24 iters) ✅ 8843 ms (24 iters) ≈ -0.7% (±10.4%) — / —
finagle-chirper 21 ✅ 5989 ms (33 iters) ✅ 5996 ms (33 iters) ≈ +0.1% (±25.4%) ⚠️ W:3 / ⚠️ W:3
finagle-chirper 25 ✅ 5542 ms (36 iters) ✅ 5550 ms (36 iters) ≈ +0.1% (±24.6%) ⚠️ W:3 / ⚠️ W:3
fj-kmeans 21 ✅ 2791 ms (67 iters) ✅ 2838 ms (66 iters) ≈ +1.7% (±2.7%) — / —
fj-kmeans 25 ✅ 2852 ms (66 iters) ✅ 2834 ms (66 iters) ≈ -0.6% (±2.6%) — / —
future-genetic 21 ✅ 2086 ms (89 iters) ✅ 2064 ms (90 iters) ≈ -1.1% (±2.6%) — / —
future-genetic 25 ✅ 2100 ms (88 iters) ✅ 2103 ms (88 iters) ≈ +0.1% (±2.5%) — / —
naive-bayes 21 ✅ 1266 ms (135 iters) ✅ 1263 ms (135 iters) ≈ -0.2% (±32.4%) — / —
naive-bayes 25 ✅ 1016 ms (168 iters) ✅ 1017 ms (168 iters) ≈ +0.1% (±31.8%) — / —
reactors 21 ✅ 16362 ms (15 iters) ✅ 16222 ms (15 iters) ≈ -0.9% (±8.7%) — / —
reactors 25 ✅ 18867 ms (15 iters) ✅ 19004 ms (15 iters) ≈ +0.7% (±4.9%) — / —
Internal counter details (ddprof)

ddprof internal counters, latest / dev (✅ = 0, · = unavailable):

Benchmark JDK Dropped rec Dropped jvmti Dropped trace Skipped WC AGCT fail Unwind fail
akka-uct 21 ✅ / ✅ ✅ / ✅ 2 / 1 1936 / 1969 ✅ / ✅ ✅ / ✅
akka-uct 25 ✅ / ✅ ✅ / ✅ ✅ / 3 2258 / 2003 ✅ / ✅ ✅ / ✅
finagle-chirper 21 ✅ / ✅ ✅ / ✅ 4 / 5 8622 / 8930 ✅ / ✅ ✅ / ✅
finagle-chirper 25 ✅ / ✅ ✅ / ✅ 1 / 2 8497 / 8401 ✅ / ✅ ✅ / ✅
fj-kmeans 21 ✅ / ✅ ✅ / ✅ 3 / 2 1283 / 1288 ✅ / ✅ ✅ / ✅
fj-kmeans 25 ✅ / ✅ ✅ / ✅ 1 / ✅ 1282 / 1292 ✅ / ✅ ✅ / ✅
future-genetic 21 ✅ / ✅ ✅ / ✅ 4 / 1 2933 / 3028 ✅ / ✅ ✅ / ✅
future-genetic 25 ✅ / ✅ ✅ / ✅ 2 / 3 2856 / 2850 ✅ / ✅ ✅ / ✅
naive-bayes 21 ✅ / ✅ ✅ / ✅ 2 / 4 3539 / 3501 ✅ / ✅ ✅ / ✅
naive-bayes 25 ✅ / ✅ ✅ / ✅ 7 / 5 3446 / 3450 ✅ / ✅ ✅ / ✅
reactors 21 ✅ / ✅ ✅ / ✅ 2 / 1 1682 / 1519 ✅ / ✅ ✅ / ✅
reactors 25 ✅ / ✅ ✅ / ✅ ✅ / ✅ 1907 / 1977 ✅ / ✅ ✅ / ✅

@dd-octo-sts

dd-octo-sts Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (commit 35c49c2)

Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/124997895 Commit: 35c49c2663a2e6e270a0287e4e059b8dab047faa

⚠️ Significant outliers

  • 🟢 fj-kmeans (JDK 25): runtime -3% (2842→2756 ms)
  • 🟢 future-genetic (JDK 21): runtime -2.9% (2142→2080 ms)
Runtime details (per benchmark × JDK)
Benchmark JDK Latest Dev Δ (dev vs latest) Issues L/D
akka-uct 21 ✅ 10367 ms (21 iters) ✅ 10371 ms (21 iters) ≈ +0% (±11.3%) — / —
akka-uct 25 ✅ 8917 ms (24 iters) ✅ 8766 ms (24 iters) ≈ -1.7% (±10.2%) — / —
finagle-chirper 21 ✅ 5986 ms (33 iters) ✅ 5972 ms (33 iters) ≈ -0.2% (±25.3%) ⚠️ W:3 / ⚠️ W:3
fj-kmeans 21 ✅ 2801 ms (67 iters) ✅ 2750 ms (68 iters) ≈ -1.8% (±2.6%) — / —
fj-kmeans 25 ✅ 2842 ms (66 iters) ✅ 2756 ms (68 iters) 🟢 -3% — / —
future-genetic 21 ✅ 2142 ms (87 iters) ✅ 2080 ms (89 iters) 🟢 -2.9% — / —
future-genetic 25 ✅ 1991 ms (94 iters) ✅ 2035 ms (91 iters) ≈ +2.2% (±2.7%) — / —
naive-bayes 25 ✅ 1025 ms (167 iters) ✅ 1010 ms (169 iters) ≈ -1.5% (±31.4%) — / —
reactors 21 ✅ 16244 ms (15 iters) ✅ 16474 ms (15 iters) ≈ +1.4% (±7.9%) — / —
reactors 25 ✅ 18288 ms (15 iters) ✅ 18332 ms (15 iters) ≈ +0.2% (±3.9%) — / —
Internal counter details (ddprof)

ddprof internal counters, latest / dev (✅ = 0, · = unavailable):

Benchmark JDK Dropped rec Dropped jvmti Dropped trace Skipped WC AGCT fail Unwind fail
akka-uct 21 ✅ / ✅ ✅ / ✅ 2 / 4 1950 / 1991 ✅ / ✅ ✅ / ✅
akka-uct 25 ✅ / ✅ ✅ / ✅ 1 / 1 2303 / 2180 ✅ / ✅ ✅ / ✅
finagle-chirper 21 ✅ / ✅ ✅ / ✅ 2 / 6 8767 / 8881 ✅ / ✅ ✅ / ✅
finagle-chirper 25 ✅ / ✅ ✅ / ✅ 2 / 1 8034 / 8300 ✅ / ✅ ✅ / ✅
fj-kmeans 21 ✅ / ✅ ✅ / ✅ 1 / 1 1316 / 1287 ✅ / ✅ ✅ / ✅
fj-kmeans 25 ✅ / ✅ ✅ / ✅ 1 / 2 1289 / 1282 ✅ / ✅ ✅ / ✅
future-genetic 21 ✅ / ✅ ✅ / ✅ 2 / ✅ 2889 / 2882 ✅ / ✅ ✅ / ✅
future-genetic 25 ✅ / ✅ ✅ / ✅ 3 / 1 2935 / 2838 ✅ / ✅ ✅ / ✅
naive-bayes 21 ✅ / ✅ ✅ / ✅ 8 / 2 3496 / 3495 ✅ / ✅ ✅ / ✅
naive-bayes 25 ✅ / ✅ ✅ / ✅ 4 / 4 3477 / 3510 ✅ / ✅ ✅ / ✅
reactors 21 ✅ / ✅ ✅ / ✅ 2 / 1 1642 / 1586 ✅ / ✅ ✅ / ✅
reactors 25 ✅ / ✅ ✅ / ✅ 1 / 3 1802 / 1759 ✅ / ✅ ✅ / ✅

@dd-octo-sts

dd-octo-sts Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (commit a9fb05f)

Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/125008807 Commit: a9fb05fe90d68c1097e48893b1256661ecdfce30

⚠️ Significant outliers

  • 🟢 fj-kmeans (JDK 21): runtime -3.1% (2772→2687 ms)
  • 💥 naive-bayes (JDK 25): latest crashed
Runtime details (per benchmark × JDK)
Benchmark JDK Latest Dev Δ (dev vs latest) Issues L/D
akka-uct 21 ✅ 10309 ms (21 iters) ✅ 10453 ms (21 iters) ≈ +1.4% (±11%) — / —
akka-uct 25 ✅ 8814 ms (24 iters) ✅ 8779 ms (24 iters) ≈ -0.4% (±10.4%) — / —
finagle-chirper 21 ✅ 6006 ms (33 iters) ✅ 6047 ms (33 iters) ≈ +0.7% (±25.8%) ⚠️ W:3 / ⚠️ W:3
finagle-chirper 25 ✅ 5467 ms (36 iters) ✅ 5455 ms (36 iters) ≈ -0.2% (±24.8%) ⚠️ W:3 / ⚠️ W:3
fj-kmeans 21 ✅ 2772 ms (68 iters) ✅ 2687 ms (70 iters) 🟢 -3.1% — / —
fj-kmeans 25 ✅ 2810 ms (66 iters) ✅ 2801 ms (66 iters) ≈ -0.3% (±2.6%) — / —
future-genetic 21 ✅ 2087 ms (88 iters) ✅ 2055 ms (90 iters) ≈ -1.5% (±2.7%) — / —
future-genetic 25 ✅ 2088 ms (89 iters) ✅ 2046 ms (91 iters) ≈ -2% (±2.5%) — / —
naive-bayes 21 ✅ 1233 ms (139 iters) ✅ 1257 ms (136 iters) ≈ +1.9% (±32.9%) — / —
naive-bayes 25 💥 0 ms (0 iters) 💥 0 ms (0 iters) — / —
reactors 21 ✅ 16947 ms (15 iters) ✅ 15936 ms (15 iters) ≈ -6% (±8.2%) — / —
reactors 25 ✅ 18681 ms (15 iters) ✅ 18731 ms (15 iters) ≈ +0.3% (±5.3%) — / —
Internal counter details (ddprof)

ddprof internal counters, latest / dev (✅ = 0, · = unavailable):

Benchmark JDK Dropped rec Dropped jvmti Dropped trace Skipped WC AGCT fail Unwind fail
akka-uct 21 ✅ / ✅ ✅ / ✅ 1 / 1 2008 / 2140 ✅ / ✅ ✅ / ✅
akka-uct 25 ✅ / ✅ ✅ / ✅ 1 / 1 2058 / 2262 ✅ / ✅ ✅ / ✅
finagle-chirper 21 ✅ / ✅ ✅ / ✅ 3 / 3 8437 / 8783 ✅ / ✅ ✅ / ✅
finagle-chirper 25 ✅ / ✅ ✅ / ✅ 1 / 3 8064 / 8228 ✅ / ✅ ✅ / ✅
fj-kmeans 21 ✅ / ✅ ✅ / ✅ 6 / ✅ 1275 / 1291 ✅ / ✅ ✅ / ✅
fj-kmeans 25 ✅ / ✅ ✅ / ✅ ✅ / 2 1250 / 1261 ✅ / ✅ ✅ / ✅
future-genetic 21 ✅ / ✅ ✅ / ✅ ✅ / 2 2876 / 3036 ✅ / ✅ ✅ / ✅
future-genetic 25 ✅ / ✅ ✅ / ✅ 2 / ✅ 2912 / 2869 ✅ / ✅ ✅ / ✅
naive-bayes 21 ✅ / ✅ ✅ / ✅ 2 / 4 3492 / 3491 ✅ / ✅ ✅ / ✅
reactors 21 ✅ / ✅ ✅ / ✅ ✅ / ✅ 1892 / 1633 ✅ / ✅ ✅ / ✅
reactors 25 ✅ / ✅ ✅ / ✅ 2 / 2 1914 / 1937 ✅ / ✅ ✅ / ✅

@dd-octo-sts

dd-octo-sts Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (commit 2033006)

Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/125072159 Commit: 20330063bf66bf581cf9ae47f94cff709e5aa6be

⚠️ Significant outliers

  • 🟢 fj-kmeans (JDK 25): runtime -3.7% (2848→2743 ms)
  • 🔴 future-genetic (JDK 25): runtime +3.2% (1994→2057 ms)
Runtime details (per benchmark × JDK)
Benchmark JDK Latest Dev Δ (dev vs latest) Issues L/D
akka-uct 21 ✅ 10363 ms (21 iters) ✅ 10147 ms (21 iters) ≈ -2.1% (±11.6%) — / —
akka-uct 25 ✅ 8966 ms (24 iters) ✅ 8873 ms (24 iters) ≈ -1% (±10.2%) — / —
finagle-chirper 21 ✅ 6007 ms (33 iters) ✅ 5986 ms (33 iters) ≈ -0.3% (±26.1%) ⚠️ W:3 / ⚠️ W:3
finagle-chirper 25 ✅ 5488 ms (36 iters) ✅ 5433 ms (36 iters) ≈ -1% (±24.3%) ⚠️ W:3 / ⚠️ W:3
fj-kmeans 21 ✅ 2726 ms (68 iters) ✅ 2760 ms (68 iters) ≈ +1.2% (±2.6%) — / —
fj-kmeans 25 ✅ 2848 ms (66 iters) ✅ 2743 ms (69 iters) 🟢 -3.7% — / —
future-genetic 21 ✅ 2062 ms (90 iters) ✅ 2047 ms (90 iters) ≈ -0.7% (±2.6%) — / —
future-genetic 25 ✅ 1994 ms (93 iters) ✅ 2057 ms (90 iters) 🔴 +3.2% — / —
naive-bayes 21 ✅ 1269 ms (135 iters) ✅ 1258 ms (136 iters) ≈ -0.9% (±32.5%) — / —
naive-bayes 25 ✅ 1012 ms (169 iters) ✅ 1016 ms (168 iters) ≈ +0.4% (±31.6%) — / —
reactors 21 ✅ 16554 ms (15 iters) ✅ 16630 ms (15 iters) ≈ +0.5% (±8.5%) — / —
reactors 25 ✅ 18407 ms (15 iters) ✅ 18308 ms (15 iters) ≈ -0.5% (±4.1%) — / —
Internal counter details (ddprof)

ddprof internal counters, latest / dev (✅ = 0, · = unavailable):

Benchmark JDK Dropped rec Dropped jvmti Dropped trace Skipped WC AGCT fail Unwind fail
akka-uct 21 ✅ / ✅ ✅ / ✅ 1 / 2 2101 / 2026 ✅ / ✅ ✅ / ✅
akka-uct 25 ✅ / ✅ ✅ / ✅ 4 / 3 2336 / 2365 ✅ / ✅ ✅ / ✅
finagle-chirper 21 ✅ / ✅ ✅ / ✅ 4 / 3 8772 / 8497 ✅ / ✅ ✅ / ✅
finagle-chirper 25 ✅ / ✅ ✅ / ✅ 1 / 1 8397 / 8470 ✅ / ✅ ✅ / ✅
fj-kmeans 21 ✅ / ✅ ✅ / ✅ 3 / 1 1260 / 1274 ✅ / ✅ ✅ / ✅
fj-kmeans 25 ✅ / ✅ ✅ / ✅ 1 / 3 1303 / 1307 ✅ / ✅ ✅ / ✅
future-genetic 21 ✅ / ✅ ✅ / ✅ 1 / 2 2893 / 2882 ✅ / ✅ ✅ / ✅
future-genetic 25 ✅ / ✅ ✅ / ✅ 1 / 2 2958 / 2912 ✅ / ✅ ✅ / ✅
naive-bayes 21 ✅ / ✅ ✅ / ✅ ✅ / 1 3540 / 3527 ✅ / ✅ ✅ / ✅
naive-bayes 25 ✅ / ✅ ✅ / ✅ 5 / 6 3458 / 3485 ✅ / ✅ ✅ / ✅
reactors 21 ✅ / ✅ ✅ / ✅ 2 / 1 1736 / 1754 ✅ / ✅ ✅ / ✅
reactors 25 ✅ / ✅ ✅ / ✅ ✅ / ✅ 1990 / 1872 ✅ / ✅ ✅ / ✅

@jbachorik
jbachorik force-pushed the jb/reference-chains branch from 2033006 to 02a0a93 Compare July 17, 2026 11:39
@dd-octo-sts

dd-octo-sts Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (commit 02a0a93)

Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/125255579 Commit: 02a0a93ab9ef8ac64aa3c023b521fb75c90101c0

✅ Within expected boundaries

No significant runtime deltas (all within run-to-run noise) and no internal-counter outliers.

Runtime details (per benchmark × JDK)
Benchmark JDK Latest Dev Δ (dev vs latest) Issues L/D
akka-uct 21 ✅ 10381 ms (21 iters) ✅ 10219 ms (21 iters) ≈ -1.6% (±10.7%) — / —
akka-uct 25 ✅ 8953 ms (24 iters) ✅ 8955 ms (24 iters) ≈ +0% (±10.3%) — / —
finagle-chirper 25 ✅ 5441 ms (36 iters) ✅ 5453 ms (36 iters) ≈ +0.2% (±24.7%) ⚠️ W:3 / ⚠️ W:4
fj-kmeans 21 ✅ 2826 ms (66 iters) ✅ 2821 ms (66 iters) ≈ -0.2% (±2.5%) — / —
fj-kmeans 25 ✅ 2823 ms (66 iters) ✅ 2831 ms (66 iters) ≈ +0.3% (±2.6%) — / —
future-genetic 21 ✅ 2108 ms (88 iters) ✅ 2101 ms (88 iters) ≈ -0.3% (±2.7%) — / —
future-genetic 25 ✅ 2058 ms (90 iters) ✅ 2058 ms (90 iters) ≈ 0% (±2.8%) — / —
naive-bayes 21 ✅ 1272 ms (135 iters) ✅ 1254 ms (136 iters) ≈ -1.4% (±32.4%) — / —
naive-bayes 25 ✅ 1025 ms (167 iters) ✅ 1030 ms (166 iters) ≈ +0.5% (±31.6%) — / —
reactors 21 ✅ 16061 ms (16 iters) ✅ 16620 ms (15 iters) ≈ +3.5% (±7%) — / —
reactors 25 ✅ 18214 ms (15 iters) ✅ 18799 ms (15 iters) ≈ +3.2% (±4.4%) — / —
Internal counter details (ddprof)

ddprof internal counters, latest / dev (✅ = 0, · = unavailable):

Benchmark JDK Dropped rec Dropped jvmti Dropped trace Skipped WC AGCT fail Unwind fail
akka-uct 21 ✅ / ✅ ✅ / ✅ 2 / 5 2081 / 1905 ✅ / ✅ ✅ / ✅
akka-uct 25 ✅ / ✅ ✅ / ✅ 1 / 4 2288 / 2409 ✅ / ✅ ✅ / ✅
finagle-chirper 25 ✅ / ✅ ✅ / ✅ 3 / 3 8386 / 8271 ✅ / ✅ ✅ / ✅
fj-kmeans 21 ✅ / ✅ ✅ / ✅ 1 / 3 1278 / 1249 ✅ / ✅ ✅ / ✅
fj-kmeans 25 ✅ / ✅ ✅ / ✅ 7 / 1 1292 / 1268 ✅ / ✅ ✅ / ✅
future-genetic 21 ✅ / ✅ ✅ / ✅ 3 / 1 2963 / 3067 ✅ / ✅ ✅ / ✅
future-genetic 25 ✅ / ✅ ✅ / ✅ 1 / ✅ 2906 / 2874 ✅ / ✅ ✅ / ✅
naive-bayes 21 ✅ / ✅ ✅ / ✅ 8 / 2 3532 / 3477 ✅ / ✅ ✅ / ✅
naive-bayes 25 ✅ / ✅ ✅ / ✅ 2 / 4 3500 / 3472 ✅ / ✅ ✅ / ✅
reactors 21 ✅ / ✅ ✅ / ✅ 2 / ✅ 1771 / 1762 ✅ / ✅ ✅ / ✅
reactors 25 ✅ / ✅ ✅ / ✅ 3 / ✅ 1879 / 1923 ✅ / ✅ ✅ / ✅

Copilot AI review requested due to automatic review settings July 21, 2026 15:22

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@datadog-datadog-prod-us1

datadog-datadog-prod-us1 Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Bits has a CI fix ready

🟢 Investigated · 🟢 Fix prepared · ⚪ Validation skipped · 🟠 Ready

symbols_linux.cpp performed pointer arithmetic on a null ELF load base for non-PIE images while generating PLT and relocation addresses. The calculations now use integer address arithmetic before conversion back to pointers.

Commit fix to this PR


View in Datadog | Reviewed commit ed6525a · Any feedback? Reach out in #deveng-pr-agent

The AggressiveLeakReferenceChainTest failed on musl CI because
resetSearchStateForTest() spent the previous test's _search_pain_ms
into _pain_budget but did not reset the budget itself. On fast CI
runners (musl, small heap, no GC pauses) the wall-clock drain between
tests was insufficient, leaving the budget non-zero and blocking
canAffordNewSearch() -> shouldRunPass() returned false before ever
reaching hasLeakSignal().

Cache the refill rate from start() in _pain_budget_refill_rate and
rebuild the budget with it in resetSearchStateForTest() so every test
starts from zero debt.
The fixed defaults (budget=1000, ttl=60s, framecap=65536) are tuned for a
small heap. On a 4 GiB heap the BFS explores only ~60k edges before
the 60s TTL expires, abandoning every search without meaningful
progress.

Add ReferenceChainTracker::autoTuneDefaults() which scales unset
defaults from the process's max heap size and available processor
count, called from start() after LivenessTracker resolves max heap
but before the frontier table is constructed. Only overrides
defaults the operator did not set explicitly (tracked by
_reference_chains_tuned_mask in Arguments).

Tuning logic:
  budget:       sqrt(heap_mib / 512) * default (4GiB -> 2x, 16GiB -> 4x)
  firstpass:    10x budget (one-shot root enumeration can afford more)
  ttl:          (heap_mib / 512) * default, clamped [60s, 30min]
  framecap:     budget-scaled (larger budget -> larger frontier)
  pausetarget: 1 + (nprocs-1)/3 * default, capped 50ms
  painbudget:   1 + (nprocs-1)/4 * default, capped 5%

hop_cap is left alone (bounds chain depth, not breadth).
…scalation

The TTL was a time-based bound, but the search's completion is a
graph-size problem. On a 4 GiB heap, the BFS needs ~2000 passes at
2000 edges/pass — the TTL (auto-tuned to 840s) kills the
search at pass 336, frontier only half full, making progress but
not done. Then the pain budget drains, restarts, and repeats —
burning CPU forever without producing chains.

Proposal 1 — progress-based termination:
Replace the TTL abandonment with a no-progress detector.
Abandon only after NO_PROGRESS_PASS_LIMIT (30) consecutive passes
with zero new frontier entries admitted (genuinely stuck, not just
slow). A large heap takes more passes simply because there are
more objects — that is not 'stuck'. Suppressed when
isUrgent() (secondsToOOM() < OOM_URGENT_THRESHOLD_S): the search
must complete before the app OOMs, bounded only by the per-pass
STW pause SLO.

Proposal 2 — urgency-driven budget escalation:
When isUrgent(), raise the per-pass pause target to
URGENT_PAUSE_TARGET_MS (50ms) and tighten cadence to
URGENT_CADENCE_NS (10ms) so passes run back-to-back.
Also raise _budget 4x so the PID controller can let each pass
explore more edges. The total STW time is the same, but the
search converges in fewer, longer passes.

The _pass_deadline_ns and updatePacing() now use
_effective_pause_target_ms instead of the static _pause_target_ms.
resetSearchStateForTest() resets _passes_since_last_progress.
Updated the TTL unit test to verify the new wiring.
Proposes replacing the whole-graph forward BFS with a pruned
forward BFS that treats leaked objects as leaves and
terminates early when all leaked candidates are found.

Key design decisions (from review):
- Marker tag must be negative (class-tag range) to avoid
  collision with frontier tags (positive jlong from
  _next_tag).
- Do not return JVMTI_VISIT_ABORT when pruning --
  that aborts the entire FollowReferences walk,
  not just the candidate subtree. Simply skip
  the admitObject() call instead.
- Pre-tag candidates in threadLoop()'s first iteration, not
  in start() -- keeps everything on the BFS
  thread.
- Cleanup via GetObjectsWithTags(MARKER_TAG) -- don't
  re-resolve jweak references (candidate may have died).
- resetSearchStateForTest() must reset the candidate set.
- Cost claim softened to 10-100x (worst case is same as
  whole-graph BFS if candidates are deep).
Replace the whole-graph forward BFS with a pruned forward
BFS that treats leaked objects as leaves and terminates
early when all leaked candidates are found.

Phase 1: Candidate state fields on ReferenceChainTracker
- MARKER_TAG_BASE (negative, disjoint from frontier tags)
- _candidate_count, _candidate_found_bits, _candidate_tags[],
  _candidate_frontier_tags[]
- Init in start(), reset in resetSearchStateForTest()

Phase 2: Pre-tag candidates in threadLoop()
- Before first runPass(), call selectLeakCandidates() and
  SetTag(obj, MARKER_TAG_BASE - i) for each candidate.
- Gated on gcGenerationsEnabled() (no-op without it).

Phase 3: Prune heapReferenceCallback() at candidates
- Check *tag_ptr <= MARKER_TAG_BASE (marker tag, negative).
- Record chain link via FrontierTable::insert() with the marker
  tag as the frontier key (JVMTI SetTag overwrites, so the
  marker tag doubles as the buildChainEvent() lookup key).
- Skip admitObject() (don't enqueue children).
- Do NOT return JVMTI_VISIT_ABORT (aborts entire walk).

Phase 4: Early termination
- After each pass, if popcount(_candidate_found_bits) ==
  _candidate_count, transition to COMPLETED.

Phase 5: Cleanup
- On completion/abandonment, GetObjectsWithTags for each marker
  tag, SetTag(obj, 0), DeleteLocalRef, Deallocate.
- Reset candidate state.

Phase 7: pollWatchedTargets() canary path
- When getTag() returns a marker tag (negative), use it directly
  for buildChainEvent() lookup instead of the normal
  frontier tag path.
With canary pruning, total STW is <20ms per 60s recording
in practice. Use that wiggle room:

- Remove cadence sleep between passes when canary
  search is active (candidates still to find).
  shouldRunPass() returns true immediately for an active
  canary search, bypassing the cadence gate.
  The PID controller self-regulates per-pass
  STW pause.

- Frontier cap hit no longer abandons the search
  immediately. The search stays RUNNING:
  existing frontier entries may still lead to
  candidates, and the no-progress detector handles
  abandonment. The frontier cap is now a memory bound,
  not a correctness bound.

Updated tests:
- FrontierCapAbandonsSearchAndReleasesTags ->
  FrontierCapHitDoesNotAbandonImmediately (verifies search
  stays RUNNING after cap hit).
- ReleaseSearchTagsFailureBlocksTagReuseUntilItSucceeds
  updated to use no-progress abandonment instead
  of frontier cap (which no longer abandons).
- ReferenceChainsTestAccessor::reset() now resets
  _passes_since_last_progress and candidate fields.
…ve tags

FrontierTable::lookup() rejects tags <= 0 (it uses tag-1
as array index). Marker tags are negative
(MARKER_TAG_BASE - i = -(1LL << 62)), so
buildChainEvent(marker_tag) could never find the
candidate's entry -- no chains were produced.

Fix: store the per-candidate chain link (parent_tag,
referrer_klass, depth) in dedicated arrays
(_candidate_parent_tags[], _candidate_referrer_klasses[],
_candidate_depths[]) at pruning time. Add
buildCanaryChainEvent(candidate_idx) that walks
parent_tag (positive, so lookup() works) through the
frontier table and prepends the candidate's
own referrer_klass. pollWatchedTargets() uses
buildCanaryChainEvent() for canary candidates
(tag <= MARKER_TAG_BASE) and buildChainEvent()
for normal candidates.
REFERENCE_CHAIN_CANDIDATE_COUNT: number of leaked candidates
pre-tagged before the walk.
REFERENCE_CHAIN_CANDIDATES_FOUND: number of candidates
found so far in the current search (popcount
of _candidate_found_bits).

Set in threadLoop() after pre-tagging and
in runPass() on canary early termination.
@jbachorik
jbachorik force-pushed the jb/reference-chains branch from c033074 to b5dc65f Compare August 19, 2026 12:56
The canary pruning check (*tag_ptr <= MARKER_TAG_BASE) was placed
AFTER the *tag_ptr < 0 class-tag check, which
returns 0 for any negative tag. Since marker
tags are negative (MARKER_TAG_BASE - i = -(1LL << 62)),
they hit the class-tag path first and never reach
the canary pruning code — candidates were
never detected, so no chains were produced.

Fix: move the canary pruning check BEFORE the
*tag_ptr < 0 class-tag check. Also fix
static member function access (use
ctx->tracker->_candidate_count instead of _candidate_count).
In heapReferenceCallback() pruning, _candidate_referrer_klasses was
set to parent.referrer_klass (the referrer's
klass), not the candidate's own klass.
The chain would have the parent's klass twice
and the candidate's klass missing.

Fix: resolve class_tag (JVMTI's klass tag
of the referent = the candidate) via classTags()->resolve()
and store that as the candidate's own klass.
When a leaked candidate is directly reachable from a GC root
(static field, JNI global, thread stack), the referrer_tag_ptr
is nullptr and rtag = 0. The 'if (rtag > 0)' check
failed, so the chain link was never recorded --
candidates_found stayed 0 and no chains were produced.

Fix: add an else branch for root-referenced candidates.
Record with parent_tag=0, depth=1, and
root_kind=reference_kind (the JVMTI
root kind for this reference). Also fix buildCanaryChainEvent
to handle parent_tag==0 (chain is just [candidate_klass]
with root_kind from the frontier entry).
… iteration

The pre-tagging was gated on !_search_started, so it only ran
once. If the search started before any candidates
existed (generations just enabled, no positive-slope klass yet),
candidates would appear later but pre-tagging
would never run -- _search_started was
already true.

Fix: remove the !_search_started gate. Pre-tag
whenever _candidate_count == 0 and candidates are available
from selectLeakCandidates(). This handles the
case where candidates appear after the search has already
started.
Logs why a klass fails the qualifying-growth check:
INSUFFICIENT_FILL (ring not full enough), SLOPE_TOO_SMALL
(mean growth below threshold), or floor_rising=false (minimum not
rising). This helps diagnose why a leak with a clear
upward population trend isn't being flagged as a
candidate.
The thirds approach (comparing earliest third vs recent third of the
ring) only uses 2/3 of the data and is noisy for
oscillating-but-growing trends. A klass that grows
steadily over 30+ GC epochs but oscillates within each
30-sample window can show a negative third-vs-third
slope even though the overall trend is clearly up.

Replace ringThirdsStats with a full-window least-squares
linear regression (y = a + b*x, x = sample position 0..fill-1).
This uses all samples and produces a proper slope
that matches what a dashboard graph shows.

Updated SecondsToOOMTest.RisingFloorProjectsExpectedSeconds:
time_delta changed from 7s (thirds: 8-1) to 9s (full window: 9-0).
Expected secondsToOOM changed from 10.0 to 9.0.
Add TEST_LOG to:
- recordHeapFloorSample: logs used bytes + timestamp for each sample
- onGC: logs when heap floor is recorded + GC epoch
- secondsToOOM: logs ring fill, bytes_delta, time_delta, means,
  and why it returns -1 (insufficient fill, not rising, etc.)
- heapFloorRising: logs mean/floor values + why it returns false
- selectLeakCandidates: logs summary (candidate count, required hysteresis,
  heapFloorRising state)
- hasLeakSignal: logs final verdict (secondsToOOM + candidates)

This gives full visibility into why the leak detector
isn't firing, in one deployment.
…or ring

The heap floor ring shows fill=0 despite onGC recording a sample.
Added TEST_LOG to recordHeapFloorSample to see whether it's
entered and whether _heap_floor_recording_disabled_for_test
is incorrectly true on the hotdog.
On JDK 26 with generational ZGC, _used_at_last_gc (read
from CollectedHeap via VMStructs) reports the committed
heap size, not the used bytes. The heap floor
would appear flat (7.5GB constant) even as the real
used bytes grow (2.19GB and rising).

Fix: in resolvePostGcHeapUsage(), when UseZGC is
enabled and hotspot_version >= 26, fall back to
HeapUsage::get(false)._used (the JMX path) instead of
_used_at_last_gc. Scoped to ZGC on JDK 26+ only —
other GC/JDK combinations keep the existing
_used_at_last_gc path.
VMFlag::find is a VMStructs table walk — don't call it on
every GC. Cache the result (UseZGC && hotspot_version >= 26)
as _is_zgc_jdk26_plus in initialize(), read it in
resolvePostGcHeapUsage().
LEAK_GROWTH_ABS_MIN was 5 (minimum 5 instances/epoch slope).
For large-population klasses (e.g. byte[] with thousands of
instances), a slope of 0.5 instances/epoch is
significant (~5% growth) but far below 5. The absolute
floor of 5 was too high, rejecting real leaks
in large-population klasses.

Lowered to 1 so only klasses with very small populations
(< 7 instances) need an absolute floor. This still
rejects single-instance oscillation noise while allowing
real leaks in large-population klasses.

Also: cache ZGC JDK26+ check in initialize() with debug logging.
selectLeakCandidates() was reading cached_slope directly without calling
hasQualifyingGrowth(), so consecutive_positive was not
updated for the current scan. The cached_slope could be stale
(set by a previous push with a negative slope), causing
selectLeakCandidates to skip the klass even though the
current trend is positive.

Fix: call hasQualifyingGrowth(entry) in selectLeakCandidates()
so consecutive_positive is updated and the slope is fresh.
Made hasQualifyingGrowth() take const ref (cached_slope is mutable).
Instead of pre-tagging candidate objects with negative marker tags
(MARKER_TAG_BASE - i), match by class_tag in heapReferenceCallback().
This avoids SetTag entirely: the walk visits every object
normally, and when it encounters one whose class matches
a candidate klass ID, it records the chain link and
skips expanding that object's children.

- _candidate_tags[i] now holds the klass ID (not marker tag)
- _candidate_frontier_tags[i] holds the frontier tag assigned
  by nextTag() when pruning
- buildCanaryChainEvent() uses _candidate_frontier_tags[i] as
  both the frontier table key and the target_tag
- pollWatchedTargets() canary path checks tag > 0 (frontier tag from
  heapReferenceCallback) instead of tag <= MARKER_TAG_BASE

Tests need updating: PollWatchedTargetsTest expects the old
marker-tag approach. 3 tests fail.
Tests now set _candidate_frontier_tags via
setCandidateFrontierTagForTest() so
buildCanaryChainEvent() can reconstruct the chain.
Added setCandidateFrontierTagForTest() test accessor.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants